package co.yixiang.yshop.module.product.service.productcouponrelation;


import co.yixiang.yshop.framework.mybatis.core.query.LambdaQueryWrapperX;
import co.yixiang.yshop.module.product.controller.admin.productcoupon.vo.SendCouponDTO;
import co.yixiang.yshop.module.product.controller.app.couponrelation.vo.CartCouponDetailVO;
import co.yixiang.yshop.module.product.controller.app.couponrelation.vo.CartCouponVO;
import co.yixiang.yshop.module.product.controller.app.couponrelation.vo.UserCouponVO;
import co.yixiang.yshop.module.product.convert.storeproductcouponrelation.StoreProductCouponRelationConvert;
import co.yixiang.yshop.module.product.dal.dataobject.productcoupon.ProductCouponCdKeyDO;
import co.yixiang.yshop.module.product.dal.dataobject.productcoupon.ProductCouponDO;
import co.yixiang.yshop.module.product.dal.dataobject.productcouponrelation.ProductCouponRelationDO;
import co.yixiang.yshop.module.product.dal.mysql.productcoupon.ProductCouponCdkeyMapper;
import co.yixiang.yshop.module.product.dal.mysql.productcouponrelation.ProductCouponRelationMapper;
import co.yixiang.yshop.module.product.enums.coupon.*;
import co.yixiang.yshop.module.product.enums.couponrelation.CouponIsUsedEnum;
import co.yixiang.yshop.module.product.enums.couponrelation.CouponSearchTypeEnum;
import co.yixiang.yshop.module.product.service.productcoupon.ProductCouponService;
import co.yixiang.yshop.module.product.service.productcouponrelation.dto.CartCouponDTO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static co.yixiang.yshop.framework.common.exception.util.ServiceExceptionUtil.exception;
import static co.yixiang.yshop.module.product.enums.ErrorCodeConstants.*;

/**
 * App优惠券记录 Service 接口
 *
 * @author moxiangrong
 */
@Service
@Validated
public class AppCouponRelationServiceImpl extends ServiceImpl<ProductCouponRelationMapper, ProductCouponRelationDO> implements AppCouponRelationService {
    @Resource
    private ProductCouponService productCouponService;
    @Resource
    private ProductCouponCdkeyMapper productCouponCdkeyMapper;

    @Override
    public Boolean receiveCoupon(Long id, Long uid) {
        return receiveCoupon(id, uid, null);
    }

    private Boolean receiveCoupon(Long id, Long userId, Long cdKeyId) {
        long count = getReceivedCount(id, userId);
        if (productCouponService.couponCheckAndUpdate(id, userId, cdKeyId, count)) {
            return updateCouponRelation(id, userId, cdKeyId);
        }
        return false;
    }

    /**
     * 更新领券关系
     */
    private Boolean updateCouponRelation(Long couponId, Long userId, Long cdKeyId) {
        ProductCouponRelationDO relationDO = ProductCouponRelationDO.builder().couponId(couponId)
                .uid(userId).build();
        relationDO.setReceiveTime(LocalDateTime.now());
        relationDO.setCdKeyId(cdKeyId);
        return save(relationDO);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Boolean receiveCouponByCode(String code, Long userId) {
        ProductCouponCdKeyDO cdKeyDO = productCouponCdkeyMapper.selectOne(
                Wrappers.<ProductCouponCdKeyDO>lambdaQuery()
                        .eq(ProductCouponCdKeyDO::getCode, code)
        );
        if (cdKeyDO == null) {
            throw exception(COUPON_CD_KEY_NO_EXIST_ERROR);
        }
        if (!CouponCdKeyStatusEnum.NO_USE.getValue().equals(cdKeyDO.getStatus())) {
            throw exception(COUPON_CD_KEY_USE_ERROR);
        }
        //领券
        Boolean result = this.receiveCoupon(cdKeyDO.getCouponId(), userId, cdKeyDO.getId());
        if (result && CouponGetTypeEnum.ONE_2_ONE_CODE.getValue() == cdKeyDO.getType()) {
            cdKeyDO.setStatus(CouponCdKeyStatusEnum.USE.getValue());
            productCouponCdkeyMapper.updateById(cdKeyDO);
        }
        return result;
    }

    public long getReceivedCount(Long id, Long uid) {
        return count(new LambdaQueryWrapperX<ProductCouponRelationDO>()
                .eq(ProductCouponRelationDO::getCouponId, id).eq(ProductCouponRelationDO::getUid, uid));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendCoupon(SendCouponDTO dto) {
        dto.getUserIdList().forEach(uid -> {
            receiveCoupon(dto.getCouponId(),uid);
        });
    }

    @Override
    public void verificationCoupon(Long id) {
        ProductCouponRelationDO productCouponDO = getById(id);
        if (productCouponDO == null) {
            throw exception(PRODUCT_COUPON_NOT_EXISTS);
        }
        // 使用优惠券
        productCouponDO.setIsUsed(CouponIsUsedEnum.USED.getValue());
        productCouponDO.setUsageTime(LocalDateTime.now());
        updateById(productCouponDO);
    }

    @Override
    public void returnCoupon(Long id) {
        // 重置优惠券状态
        update(new LambdaUpdateWrapper<ProductCouponRelationDO>()
                .set(ProductCouponRelationDO::getIsUsed, CouponIsUsedEnum.NOT_USED.getValue())
                .set(ProductCouponRelationDO::getUsageTime, null)
                .eq(ProductCouponRelationDO::getId, id));
    }

    @Override
    public List<UserCouponVO> searchUserCoupon(Integer type,Long uid, Long id) {
        List<UserCouponVO> userCouponVOS = this.baseMapper.searchUserCoupon(type, uid, id);
        for (UserCouponVO userCouponVO : userCouponVOS) {
            if (CouponExpirationEnum.BY_DAY.getValue().equals(userCouponVO.getExpirationType())) {
                // 计算失效时间
                LocalDateTime expirationTime =
                        userCouponVO.getReceiveTime().plus(userCouponVO.getExpirationDay(), ChronoUnit.DAYS);
                userCouponVO.setTakingEffectTime(userCouponVO.getReceiveTime());
                userCouponVO.setExpirationTime(expirationTime);
            }
        }
        return userCouponVOS;
    }

    @Override
    public List<CartCouponVO> searchCartCoupon(List<CartCouponDTO> cartCouponDtoList, Long uid, Long id) {
        List<CartCouponVO> cartCouponVOList = new ArrayList<>();
        // 获取购物车中商品id集合
        Set<Long> productIds =
                cartCouponDtoList.stream().map(CartCouponDTO::getProductId).collect(Collectors.toSet());
        Map<Long, List<CartCouponDTO>> cartCouponDtoMap =
                cartCouponDtoList.stream().collect(Collectors.groupingBy(CartCouponDTO::getProductId));
        // 查询所有可使用的优惠券
        List<UserCouponVO> userCouponVOList = searchUserCoupon(CouponSearchTypeEnum.CAN_USE.getValue(),uid, id);
        if (CollectionUtils.isEmpty(userCouponVOList)) {
            return cartCouponVOList;
        }
        cartCouponVOList = StoreProductCouponRelationConvert.INSTANCE.convert(userCouponVOList);
        // 过滤出可以使用的优惠券并计算优惠金额
        cartCouponVOList = cartCouponVOList.stream().filter(vo -> filter(productIds, vo, cartCouponDtoMap)).collect(Collectors.toList());
        // 优惠金额降序
        cartCouponVOList = cartCouponVOList.stream()
                .sorted(Comparator.comparing(CartCouponVO::getDiscountAmount).reversed()).collect(Collectors.toList());
        return cartCouponVOList;
    }

    @Override
    public List<Long> getProductIdList(Long id) {
        List<Long> productIdList = new ArrayList<>();
        ProductCouponRelationDO couponRelationDO = getById(id);
        ProductCouponDO couponDO = productCouponService.findById(couponRelationDO.getCouponId());
        if (CouponScopeEnum.PRODUCT.getValue().equals(couponDO.getCouponScope())) {
            for (String s : couponDO.getScopeValues().split(",")) productIdList.add(Long.parseLong(s));
        }
        return productIdList;
    }

    @Override
    public Integer getCouponNumber(Long userId) {
        return this.baseMapper.getCouponNumber(userId);
    }


    /**
     * 过滤出可以使用的优惠券
     *
     * @param productIds       商品id集合
     * @param cartCouponVO     优惠券信息
     * @param cartCouponDtoMap 金额map
     * @return 过滤标识
     */
    private Boolean filter(Set<Long> productIds, CartCouponVO cartCouponVO, Map<Long, List<CartCouponDTO>> cartCouponDtoMap) {
        if (CouponScopeEnum.PRODUCT.getValue().equals(cartCouponVO.getCouponScope())) {
            Set<Long> ids = new HashSet<>();
            for (String s : cartCouponVO.getScopeValues().split(",")) ids.add(Long.parseLong(s));
            // 取交集
            ids.retainAll(productIds);
            if (CollectionUtils.isEmpty(ids)) return false;
            return compute(ids, cartCouponVO, cartCouponDtoMap);
        } else {
            return compute(productIds, cartCouponVO, cartCouponDtoMap);
        }
    }

    /**
     * 计算优惠金额
     *
     * @param productIds       商品id集合
     * @param cartCouponVO     优惠券信息
     * @param cartCouponDtoMap 金额map
     * @return 过滤标识
     */
    private Boolean compute(Set<Long> productIds, CartCouponVO cartCouponVO, Map<Long, List<CartCouponDTO>> cartCouponDtoMap) {
        // 累计金额
        BigDecimal amount = BigDecimal.ZERO;
        for (Long id : productIds)
            amount = amount.add(cartCouponDtoMap.get(id).stream().map(CartCouponDTO::getPrice).reduce(BigDecimal::add).get());
        // 是否满足门槛
        if (amount.compareTo(cartCouponVO.getThreshold()) < 0) return Boolean.FALSE;
        boolean couponTypeFlag = CouponTypeEnum.FULL_REDUCTION.getValue().equals(cartCouponVO.getCouponType());
        // 计算优惠金额
        BigDecimal discountAmount = BigDecimal.ZERO;
        List<CartCouponDetailVO> detailVOList = new ArrayList<>();
        for (Long id : productIds) {
            BigDecimal productCoupon = null;
            for (CartCouponDTO cartCouponDto : cartCouponDtoMap.get(id)) {
                if (couponTypeFlag) {
                    // 优惠金额不可超出商品总价
                    if (amount.compareTo(cartCouponVO.getCouponValue()) < 0) {
                        productCoupon = cartCouponDto.getPrice();
                    } else {
                        productCoupon = cartCouponVO.getCouponValue().multiply(cartCouponDto.getPrice().divide(amount, BigDecimal.ROUND_HALF_UP));
                    }
                } else {
                    productCoupon = cartCouponDto.getPrice().subtract(cartCouponDto.getPrice().multiply(cartCouponVO.getDiscount()).divide(BigDecimal.TEN));
                }
                discountAmount = discountAmount.add(productCoupon);
                detailVOList.add(CartCouponDetailVO.builder().productId(id).productAttrUnique(cartCouponDto.getProductAttrUnique()).productCoupon(productCoupon).build());
            }
        }
        cartCouponVO.setDiscountAmount(discountAmount);
        cartCouponVO.setDetailVOList(detailVOList);
        return Boolean.TRUE;
    }


}
